package com.raymond.algorithm.currentlimit;

/**
 * 漏桶算法
 * 一.思想
 * 我们有一个固定容量的桶，
 * 有水流进来，也有水流出去。
 * 对于流进来的水来说，我们无法预计一共有多少水会流进来，也无法预计水流的速度。
 * 但是对于流出去的水来说，这个桶可以固定水流出的速率。
 * 而且，当桶满了之后，多余的水将会溢出。
 *
 * 二.并发数:
 * 桶里没有水的情况下:桶的大小 + 每秒流失速度(每秒请求数)
 * 桶里有水情况下:桶的大小 - 桶的已有水量 + 每秒流失速度
 *
 * 三.实现
 * 定义一个桶的大小
 * 定义一个水流速率
 * 当桶里来水的时候,判断当前桶里的水是否满了,满了水就存放不进来溢出(并发已满)
 * 桶里的水未满,当前桶里的水加1
 * 桶中的水计算公式:当前桶已有的水量 = 上次桶中的水量 - 这段时间流出的水量
 *
 *
 * @author :  raymond
 * @version :  V1.0
 * @date :  2021-04-27 18:23
 */
public class LeakyBucket {
    private long time = System.currentTimeMillis();
    /**
     * 桶的容量
     */
    private long capacity;
    /**
     * 桶的流速(每秒流速)
     */
    private long rate;
    /**
     * 当前水的容量
     */
    private double water;

    public LeakyBucket(long capacity, long rate) {
        this.capacity = capacity;
        this.rate = rate;
    }

    public boolean grant() {
        long now = System.currentTimeMillis();
        long interval = now - time;
        water = Math.max(0, water * 1000 - interval * rate) / 1000;
        time = now;
        if ((water + 1) < capacity) {
            water++;
            //水未满
            return false;
        }
        //水满
        return true;
    }

    public static void main(String[] args) throws InterruptedException {
        LeakyBucket leakyBucket = new LeakyBucket(100, 10000);

        long start = System.currentTimeMillis();
        for (int i = 0; i < 50006; i++) {
            boolean grant = leakyBucket.grant();
//            Thread.sleep(1);
            if (grant) {
                i--;
                System.out.println("进入休眠");
                Thread.sleep(10);
            }
//            System.out.println(i);
//            System.out.println(System.currentTimeMillis() - start);
        }
        System.out.println(System.currentTimeMillis() - start);
    }
}
